<?php
/**
 * 中医药基础用户信息同步
 * Created by PhpStorm.
 * User: zym/wika
 * Date: 2017/7/13
 * Time: 13:33
 */
namespace Task\App\Zyy;
use Task\Core\Base;
use \Exception;
class ZyyUserData extends Base{
    public $mysql;
    public $accountId;
    public $md5key; //加密md5秘钥
    public $interfaceUrl; //接口地址
    public function __construct()
    {
        parent::__construct();
        $this->mysql = $this->selectDB('mysql');
        $this->accountId = 38;
        $mD5Key =$this->mysql->get('one_card','md5_key',array('AND'=>array('account_id'=>$this->accountId,'status'=>1)));
        if(!$mD5Key){
            wLog('md5_key get sql: '.$this->mysql->last_query(),'Zyy/ZyyUserData');
            exit('md5_key is null');
        }
        $this->md5key = $mD5Key; //加密秘钥，和中医药一卡通一致
        $this->interfaceUrl = 'http://61.164.34.220/home/ZyyUser/interfaceEnter'; //接口地址
        //$this->interfaceUrl = 'http://192.168.21.39/home/ZyyUser/interfaceEnter'; //
    }

    /**
     * 测试medoo
     */
    public function testMedoo(){
        $tj_time = strtotime(date('Y-m-d')) - 86400;
        $count = $this->mysql->get("user_log_count", "count",
            array(
                "AND" => array(
                    "account_id" => $this->accountId,
                    "type" => 1,
                    "student_type" => 0,
                    "tj_time" => $tj_time
                ),
                "ORDER" => array(
                    "id" => "DESC"
                )
            ));
        var_dump($this->mysql->error());
        var_dump($count);
    }

    /**
     * 测试
     */
    public function test(){
        //$this->updateUserWeek('XX_YXSDWJBSJ','DWH','DWMC','PX',0,1);
        echo date('N');
    }

    /**
     * 定时入口
     */
    public function crontabEnter(){
        $time = time();
        $startTime = strtotime(date('Y-m-d 02:50:00'));
        $endTime = strtotime(date('Y-m-d 03:10:00'));
        if(date('N') == 1){ //用户信息周全表更新（每周一凌晨3:00定时）
            if($time >= $startTime  && $time <= $endTime){
                //学生
                $this->updateUserWeek('XS_XSJBSJ','XH','XM','',0,1);

                //研究生
                $this->updateUserWeek('YJS_XSJBSJ','XH','XM','',0,2);

                //教职工
                $this->updateUserWeek('JG_JZGJBSJ','GH','XM','',1,0);
            }
        } else { //新增用户更新（每天{周一除外}凌晨3：00定时）
            if($time >= $startTime && $time <= $endTime){
                //学生
                $this->updateUserDay('XS_XSJBSJ','XH','XM','',0,1);

                //研究生
                $this->updateUserDay('YJS_XSJBSJ','XH','XM','',0,2);

                //教职工
                $this->updateUserDay('JG_JZGJBSJ','GH','XM','',1,0);
            }
        }
    }

    /**
     * 用户信息周全表更新（每周一凌晨3:00定时）
     * @param $tablename 第三方用户信息表名s
     * @param $number 学工号字段
     * @param $username 姓名字段
     * @param $phone 手机号字段
     * @param int $type 身份标识，0学生，1教职工，2来宾
     * @param int $student_type 学生类型(默认0 1：本科生 :2：研究生)
     */
    public function updateUserWeek($tablename,$number,$username,$phone,$type = 0,$student_type = 0){
        //调用接口获取第三方用户总数量，用于分页查询
        $otherCount = $this->getUserCount($tablename);
        wlog('updateUserWeek otherCount: '.$otherCount,'Zyy/ZyyUserData');
        if($otherCount <= 0){
            return false;
        }
        //设置分页参数
        $pageNum = 2000;
        $pageCount = ceil($otherCount/$pageNum);
        $start = 1;

        //获取本地用户数据
        $condition = array(
            'AND'=>array(
                'account_id' => $this->accountId,
                'type' => $type,
                'student_type' => $student_type
            )
        );
        $dataLocalUser = $this->mysql->select('user',array('user_id','number'),$condition);

        //重组成一位数组，便于数组搜索
        $newLoacalUser = array();
        foreach ($dataLocalUser as $k=>$v){
            $newLoacalUser[$v['user_id']] = $v['number'];
        }

        $log_add_count = 0; //记录添加数量
        $log_update_count = 0; //记录修改数量
        $log_user_count = $otherCount; //记录总数量
        try {
            //分页循环查询,每处理{$pageNum}条休眠1秒，释放内存
            while ($start <= $pageCount) {
                $start_num = $pageNum * ($start - 1) + 1;
                $end_num = $pageNum * $start;
                //获取第三方用户信息(只需要学工号、姓名、手机)
                $dataOtherUser = $this->getUserInfoForPage($start_num, $end_num, $tablename, $number, $username, $phone);
                foreach ($dataOtherUser as $k => $v) {
                    //在本地查找是否存在，此处不能再次去查表，要不然几万次查询服务器压力大，可能会影响其他正常业务
                    $userId = array_search($v[strtolower($number)], $newLoacalUser);
                    if ($userId) { //存在更新
                        $this->mysql->update('user', array('real_name' => $v[strtolower($username)],'type' => $type,'student_type' => $student_type,'update_time' => time()),array('user_id' => $userId));
                        $log_update_count++;
                    } else { //不存在添加
                        $insertData[] = array(
                            'account_id' => $this->accountId,
                            'type' => $type,
                            'real_name' => $v[strtolower($username)],
                            'number' => $v[strtolower($number)],
                            'mobile' => $phone?$v[strtolower($phone)]:'',
                            'create_time' => time(),
                            'student_type' => $student_type
                        );
                        $log_add_count++;
                    }
                }
                $start++;
                sleep(1);
            }
            //批量插入比单条循环插入速度快5-6倍
            if(isset($insertData) && !empty($insertData)){
                $this->mysql->insert('user', $insertData);
            }
        } catch (Exception $ex) { //异常记录
            wlog('updateUserWeek Exception code: '.$ex->getCode().' message: '.$ex->getMessage(),'Zyy/ZyyUserData');
            return false;
        }

        //添加操作统计数据记录
        $typeDesc = '';
        if ($type == 1) {
            $typeDesc = "教职工周更新";
        } elseif ($type == 0) {
            $typeDesc = "学生周更新";
            if($student_type == 2){
                $typeDesc = "研究生周更新";
            } elseif ($student_type == 1) {
                $typeDesc = "本科生周更新";
            }
        }
        $this->dbUserLogAdd($log_user_count,$log_add_count,$log_update_count,$type,$student_type,$typeDesc);
    }

    /**
     * 新增用户更新（每天{周一除外}凌晨3：00定时）
     * @param $tablename 第三方用户信息表名
     * @param $number 学工号字段
     * @param $username 姓名字段
     * @param $phone 手机号字段
     * @param int $type 身份标识，0学生，1教职工，2来宾
     * @param int $student_type 学生类型(默认0 1：本科生 :2：研究生)
     */
    public function updateUserDay($tablename,$number,$username,$phone,$type = 0,$student_type = 0)
    {
        set_time_limit(0);
        //获取第三方统计数据
        $otherCount = $this->getUserCount($tablename);
        wlog('updateUserDay otherCount: '.$otherCount,'Zyy/ZyyUserData');
        if ($otherCount <= 0) {
            return false;
        }
        $tj_time = strtotime(date('Y-m-d')) - 86400;
        //查询上一次记录时第三方的用户总数
        $localCount = $this->dbUserLogQueryForCount($type,$student_type, $tj_time);
        wlog('updateUserDay localCount: '.$localCount,'Zyy/ZyyUserData');
        if ($otherCount <= $localCount) {
            return false;
        }

        //设置分页参数
        $pageNum = 2000;
        $pageCount = ceil(($otherCount - $localCount)/$pageNum);
        $start = 1;

        //获取本地用户数据
        $condition = array(
            'AND'=>array(
                'account_id' => $this->accountId,
                'type' => $type,
                'student_type' => $student_type
            )
        );
        $dataLocalUser = $this->mysql->select('user',array('user_id','number'),$condition);

        //重组成一位数组，便于数组搜索
        $newLoacalUser = array();
        foreach ($dataLocalUser as $k=>$v){
            $newLoacalUser[$v['user_id']] = $v['number'];
        }
        $log_add_count = 0; //记录添加数量
        $log_update_count = 0; //记录修改数量
        $log_user_count = $otherCount; //记录总数量
        try {
            //分页循环查询,每处理{$pageNum}条休眠1秒，释放内存
            while ($start <= $pageCount) {
                $start_num =$localCount + $pageNum * ($start - 1) + 1;
                $end_num = $localCount + $pageNum * $start;
                //获取第三方用户信息(只需要学工号、姓名、手机)
                $dataOtherUser = $this->getUserInfoForPage($start_num, $end_num, $tablename, $number, $username, $phone);
                foreach ($dataOtherUser as $k => $v) {
                    //在本地查找是否存在，此处不能再次去查表，要不然几万次查询服务器压力大，可能会影响其他正常业务
                    $userId = array_search($v[strtolower($number)], $newLoacalUser);
                    if ($userId) { //存在更新
                        $this->mysql->update('user', array('real_name' => $v[strtolower($username)],'type' => $type,'student_type' => $student_type,'update_time' => time()),array('user_id' => $userId));
                        $log_update_count++;
                    } else { //不存在添加
                        $insertData[] = array(
                            'account_id' => $this->accountId,
                            'type' => $type,
                            'real_name' => $v[strtolower($username)],
                            'number' => $v[strtolower($number)],
                            'mobile' => $phone?$v[strtolower($phone)]:'',
                            'create_time' => time(),
                            'student_type' => $student_type
                        );
                        $log_add_count++;
                    }
                }
                $start++;
                sleep(1);
            }
            //批量插入比单条循环插入速度快5-6倍
            if(isset($insertData) && !empty($insertData)){
                $this->mysql->insert('user', $insertData);
            }
        } catch (Exception $ex) { //异常记录
            wlog('updateUserDay Exception code: '.$ex->getCode().' message: '.$ex->getMessage(),'Zyy/ZyyUserData');
            return false;
        }

        $typeDesc = '';
        if ($type == 1) {
            $typeDesc = "教职工日更新";
        } elseif ($type == 0) {
            $typeDesc = "学生日更新";
            if ($student_type == 2) {
                $typeDesc = "研究生日更新";
            } elseif ($student_type == 1) {
                $typeDesc = "本科生日更新";
            }
        }
        $this->dbUserLogAdd($log_user_count,$log_add_count,$log_update_count,$type,$student_type,$typeDesc);
    }

    /**
     * 获取第三方用户总数量
     */
    public function getUserCount($tablename)
    {
        $params = array(
            'function_name'=>'getUserCount', //接口方法
            'table_name'=> $tablename, //要查询的表
            'time'=> time() //md5时间戳
        );
        //请求接口
        $res = $this->apiExecute($params);
        wlog('getUserCount res result: '.var_export($res,true),'Zyy/ZyyUserData');
        if(isset($res['status']) && $res['status'] == 'F'){
            return false;
        }
        //验证签名
        $resSign = $this->verifySign($res);
        if ($resSign['status'] != 'S') {
            wlog('getUserCount verifySign result false: '.var_export($resSign,true),'Zyy/ZyyUserData');
            return false;
        }
        if ($res['status'] != 1) {
            wlog('getUserCount request result false: '.var_export($resSign,true),'Zyy/ZyyUserData');
            return false;
        }
        return $res['data'];
    }

    /**
     * 获取第三方用户数据(分页查询接口)
     */
    public function getUserInfoForPage($start_num,$end_num,$tablename,$number,$username,$phone){
        $params = array(
            'function_name'=>'getUserInfoForPage', //接口方法
            'table_name' => $tablename,
            'field' => "$number,$username,$phone",
            'start_num' => $start_num,
            'end_num' => $end_num,
            'time' => time(),
        );
        //请求接口
        $res = $this->apiExecute($params);
        if(isset($res['status']) && $res['status'] == 'F'){
            wlog('getUserInfoForPage res false: '.var_export($res,true),'Zyy/ZyyUserData');
            return false;
        }
        wlog('getUserInfoForPage res count: '.count($res['data']),'Zyy/ZyyUserData');
        //验证签名
        $resSign = $this->verifySign($res);
        if ($resSign['status'] != 'S') {
            wlog('getUserInfoForPage verifySign result false: '.var_export($res,true),'Zyy/ZyyUserData');
            return false;
        }
        if ($res['status'] != 1) {
            wlog('getUserInfoForPage request resul falset: '.var_export($res,true),'Zyy/ZyyUserData');
            return false;
        }
        return $res['data'];
    }

    /**
     * 添加用户信息操作记录
     * @param $log_user_count 本次统计的第三方用户总人数
     * @param $log_add_count 本次统计增加的数量
     * @param $log_update_count 本次统计修改的数量
     * @param $type 更新类型 1:学生日更新 2：研究生日更新 3:教职工日更新  11:学生周更新 12：研究生周更新 13:教职工周更新
     * @param $type_desc 更新类型描述
     */
    public function dbUserLogAdd($log_user_count,$log_add_count,$log_update_count,$type,$student_type,$type_desc){
        $insertData = array(
            'account_id' => $this->accountId,
            'count' => $log_user_count,
            'add_count' => $log_add_count,
            'update_count' => $log_update_count,
            'type' => $type,
            'student_type' => $student_type,
            'type_desc' => $type_desc,
            'tj_time' => strtotime(date('Y-m-d')),
            'create_date' => date('Y-m-d H:i:s')
        );
        $this->mysql->insert('user_log_count',$insertData);
    }

    /**
     * 查询日志记录数量
     * @param $type
     * @param int $tj_time
     * @return mixed
     */
    public function dbUserLogQueryForCount($type,$student_type,$tj_time = 0)
    {
        $count = $this->mysql->get("user_log_count", "count",
            array(
                "AND" => array(
                    "account_id" => $this->accountId,
                    "type" => $type,
                    "student_type" => $student_type
                    //"tj_time" => $tj_time 
                ),
                "ORDER" => array(
                    "id" => "DESC"
                )
            ));
        return $count;
    }

    /**
     * api执行函数
     * @param $params 请求参数
     * @return array|mixed
     */
    public function apiExecute($params){
        $exeParams = $this->sign($params);
        return $this->curlPost($this->interfaceUrl,$exeParams);
    }

    /**
     * curl post请求
     * @param string $url 请求地址
     * @param string $param 请求参数
     * @return array|mixed
     */
    public function curlPost($url = '',$param = ''){
        $curl = curl_init(); // 启动一个CURL会话
        curl_setopt($curl, CURLOPT_URL, $url); // 要访问的地址
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); // 对认证证书来源的检查
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); // 从证书中检查SSL加密算法是否存在
        curl_setopt($curl, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)"); // 模拟用户使用的浏览器
        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); // 使用自动跳转
        curl_setopt($curl, CURLOPT_AUTOREFERER, 1); // 自动设置Referer
        curl_setopt($curl, CURLOPT_POST, 1); // 发送一个常规的Post请求
        curl_setopt($curl, CURLOPT_POSTFIELDS, $param); // Post提交的数据包
        curl_setopt($curl, CURLOPT_TIMEOUT, 60); // 设置超时限制防止死循环
        curl_setopt($curl, CURLOPT_HEADER, 0); // 显示返回的Header区域内容
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 获取的信息以文件流的形式返回
        $output = curl_exec($curl); // 执行操作
        if (curl_errno($curl)) {
            wlog('errno'.curl_errno($curl).' error: '. curl_error($curl),'Zyy/ZyyUserData');
            return array('status'=>'F','message'=>'请求出错');
        }
        curl_close($curl); // 关闭CURL会话
        return json_decode($output,true); // 返回数据
    }

    /**
     * 签名
     * @param $params 待签名参数
     * @return mixed
     */
    public function sign($params){
        $arr = $params;
        foreach ($arr as $k=>$v) {
            $arr[$k] = base64_encode($v);
        }
        $signStr = implode($arr);
        $sign = md5(base64_decode($this->md5key).$signStr);
        $params['sign'] = trim($sign);
        return $params;
    }

    /**
     * 验证签名
     * @param $params 待验证参数(成员必须有签名sign)
     * @return array
     */
    public function verifySign($params){
        $sign = $params['sign'];
        unset($params['sign']);
        //签名验证
        foreach ($params as $k=>$v){
            if(is_array($v)){
                $v = json_encode($v);
            }
            $params[$k]= base64_encode($v);
        }

        $strSign = implode($params);
        $resSign = trim(md5(base64_decode($this->md5key).$strSign));
        if($resSign == $sign){
            return array('status'=>'S','sign'=>$resSign);
        } else {
            return array('status'=>'F','message'=>'签名错误(客户端)');
        }
    }
}
